package com.ruoyi.common.utils.poi;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Hutool Excel 工具
 */
@Slf4j
public abstract class HTExcelUtil {

    /**
     * 导出excel文件
     * @param clazz
     * @param datas
     * @param keys
     * @param <T>
     * @return
     */
    public static <T> AjaxResult<String> exportExcel(Class<T> clazz, List<T> datas, Set<String> keys) {
        return exportExcel(clazz, datas, keys, null);
    }

    /**
     * 导出excel文件
     * @param datas
     * @param filename
     * @param keys
     * @param <T>
     * @return
     */
    public static <T> AjaxResult<String> exportExcel(Class<T> clazz, List<T> datas, Set<String> keys, String filename) {
        if (StrUtil.isEmpty(filename)) {
            if (clazz.isAnnotationPresent(ApiModel.class)) {
                filename = clazz.getAnnotation(ApiModel.class).value();
            } else {
                filename = String.valueOf(System.currentTimeMillis());
            }
        }
        ExcelWriter writer = cn.hutool.poi.excel.ExcelUtil.getWriter(true);
        List<ExField> fields = Arrays.stream(ReflectUtil.getFields(clazz))
                .filter(f -> !f.isAnnotationPresent(JsonIgnore.class))
                .filter(f -> f.isAnnotationPresent(ApiModelProperty.class))
                .filter(f -> (keys == null || keys.isEmpty() || keys.contains(f.getName())))
                .map(ExField::new)
                .collect(Collectors.toList());

        writer.setColumnWidth(-1, 20);
        writer.writeHeadRow(fields.stream().map(ExField::getTitle).collect(Collectors.toList()));
        for (T data : datas) {
            List<String> row = fields.stream().map(f -> {
                Object o = ReflectUtil.getFieldValue(data, f.getField());
                if (null == o) {
                    return "";
                }
                if (f.isDict()) {
                    String s = String.valueOf(o);
                    return f.getDicts().getOrDefault(s, "");
                }
                if (o instanceof BigDecimal) {
                    return fmtNumber((BigDecimal) o);
                }
                if (o instanceof Date) {
                    return fmtDate((Date) o);
                }
                return String.valueOf(o);
            }).collect(Collectors.toList());
            writer.writeRow(row);
        }
        return flush(writer, filename);
    }

    /**
     * 导出excel文件,并返回文件名称
     * @param wr
     * @param filename
     * @return
     */
    public static AjaxResult<String> flush(ExcelWriter wr, String filename) {
        String fname = ExcelUtil.encodingFilename(filename);
        String fpath = ExcelUtil.getAbsoluteFile(fname);
        wr.flush(new File(fpath));
        return AjaxResult.success(fname);
    }

    private static String fmtNumber(BigDecimal d) {
        return d.setScale(4, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString();
    }

    private static String fmtDate(Date date) {
        return DateUtil.format(date, "yyyy-MM-dd HH:mm:ss");
    }


    @Data
    @Accessors(chain = true)
    private static class ExField {
        Field field;
        String key;
        String title;
        boolean isDict = false;
        Map<String, String> dicts = new HashMap<>();

        public ExField(Field field) {
            this.field = field;
            this.key = field.getName();
            if (field.isAnnotationPresent(ApiModelProperty.class)) {
                ApiModelProperty an = field.getAnnotation(ApiModelProperty.class);
                String anval = an.value();
                int rmidx = anval.indexOf("（");
                if (rmidx <= 0) {
                    this.title = anval;
                } else {
                    this.title = anval.substring(0, rmidx);
                    String remarks = StringUtils.substringBetween(anval, "（", "）");
                    this.dicts = Arrays.stream(remarks.split(" "))
                            .map(StringUtils::asKV)
                            .map(s -> s.split("="))
                            .collect(Collectors.toMap(s -> s[0], s -> s[1], (a, b) -> b));
                    this.isDict = true;
                }
            }
        }
    }

}
